package com.sysfrog.cucumber.psi.psiElements;

/*
* Copyright (C) 2010 Thomas Heggelund
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*
*/

import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
import com.sysfrog.cucumber.CucumberIcons;
import com.sysfrog.cucumber.psi.CucumberFile;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.util.HashSet;
import java.util.Set;

public class CucumberStep extends CucumberElement {
    private CucumberStepMeat reference;

    public CucumberStep(@NotNull final ASTNode node) {
        super(node);
        reference = new CucumberStepMeat(this);
    }

    public PsiReference getReference() {
        return reference;
    }

    public Icon getIcon(int flags) {
        return CucumberIcons.CUCUMBER_STEP_ICON;
    }

    private class CucumberStepMeat extends PsiReferenceBase<CucumberStep> {
        public CucumberStepMeat(CucumberStep element) {
            super(element);
        }

        public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
            return super.handleElementRename(newElementName);
        }

        public PsiElement resolve() {
            return walkTree(myElement.getParent());
        }

        private PsiElement walkTree(PsiElement e) {
            if (e == null) {
                VirtualFile[] roots = ProjectRootManager.getInstance(myElement.getProject()).getContentRoots();
                return search(roots, myElement.getProject());
            } else if (e instanceof CucumberFile) {
                for (PsiElement def : e.getChildren()) {
                    if (nameMatches(def)) {
                        return def;
                    }
                }
            }
            return walkTree(e.getParent());
        }

        private PsiElement search(VirtualFile[] children, Project p) {
            for (VirtualFile file : children) {
                PsiElement e = file.isDirectory() ? search(file.getChildren(), p) : search(file, p);
                if (e != null)
                    return e;
            }
            return null;
        }

        private PsiElement search(VirtualFile file, Project project) {
            if (!"feature".equalsIgnoreCase(file.getExtension())) {
                return null;
            }
            PsiDocumentManager.getInstance(project).commitAllDocuments();
            return search(PsiManager.getInstance(project).findFile(file).getChildren());
        }

        private PsiElement search(PsiElement[] elements) {
            for (PsiElement e : elements) {
                if (nameMatches(e)) return e;
                PsiElement el = search(e.getChildren());
                if (el != null) return el;
            }
            return null;
        }


        private Set tags;

        private void getTags() {
            tags = new HashSet<String>();
            VirtualFile[] roots = ProjectRootManager.getInstance(myElement.getProject()).getContentRoots();
            searchTags(roots, myElement.getProject());
        }

        private void searchTags(VirtualFile[] children, Project p) {
            for (VirtualFile file : children) {
                if (file.isDirectory()) {
                    searchTags(file.getChildren(), p);
                } else {
                    searchTags(file, p);
                }
            }
        }

        private void searchTags(VirtualFile file, Project project) {
            if ("feature".equalsIgnoreCase(file.getExtension())) {
                PsiDocumentManager.getInstance(project).commitAllDocuments();
                searchTags(PsiManager.getInstance(project).findFile(file).getChildren());
            }
        }

        private void searchTags(PsiElement[] elements) {
            for (PsiElement e : elements) {
                if ( e instanceof ASTWrapperPsiElement ) {
                    PsiElement[] childern = e.getChildren();
                    for (PsiElement child : childern) {
                        try {
                            String rawStepMeat = child.getChildren()[0].getText();

                            String stepMeat = removeVariables( rawStepMeat );
                            tags.add( stepMeat );
                        } catch ( Exception exception ) {
                            // TODO do some better exception handling here, dette fungerer men er ikke helt bra
                        }
                    }
                }
            }
        }

        private String removeVariables( String stepMeatRaw ) {
            // TODO maybe have something like this (\d+) and (\s+) need to figure out what type it is
            return stepMeatRaw.replaceAll( "\"([^<]*)\"", "\"\""  ).trim();
        }

        private boolean nameMatches(PsiElement e) {
            return e instanceof CucumberStepMeat &&  e.getText() != null && e.getText().equals(myElement.getText());
        }

        public TextRange getRangeInElement() {
            return new TextRange(0, myElement.getTextLength());
        }

        public Object[] getVariants( ) {
            getTags();
            return this.tags.toArray();
        }
    }
}